/*
 * sim_tmxr.h: terminal multiplexer definitions
 *
 * vim: filetype=c:tabstop=4:ai:expandtab
 * SPDX-License-Identifier: MIT
 * scspell-id: ecf608fb-f62a-11ec-bbba-80ee73e9b8e7
 *
 * ---------------------------------------------------------------------------
 *
 * Copyright (c) 2001-2008 Robert M. Supnik
 * Copyright (c) 2021-2025 The DPS8M Development Team
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Robert M. Supnik shall
 * not be used in advertising or otherwise to promote the sale, use or
 * other dealings in this Software without prior written authorization from
 * Robert M. Supnik.
 *
 * ---------------------------------------------------------------------------
 */

/*
 *  Based on the original DZ11 simulator by Thord Nilson, as updated by
 *  Arthur Krewat.
 */

#if !defined(SIM_TMXR_H_)
# define SIM_TMXR_H_    0

# if !defined(SIMH_SERHANDLE_DEFINED)
#  define SIMH_SERHANDLE_DEFINED 0
typedef struct SERPORT *SERHANDLE;
# endif

# include "sim_sock.h"

# define TMXR_V_VALID    15
# define TMXR_VALID      (1 << TMXR_V_VALID)
# define TMXR_MAXBUF     256                    /* buffer size */

# define TMXR_DTR_DROP_TIME               500   /* milliseconds to drop DTR for 'pseudo' modem control */
# define TMXR_MODEM_RING_TIME               3   /* seconds to wait for DTR for incoming connections */
# define TMXR_DEFAULT_CONNECT_POLL_INTERVAL 1   /* seconds between connection polls */

# define TMXR_DBG_XMT    0x0010000    /* Debug Transmit Data */
# define TMXR_DBG_RCV    0x0020000    /* Debug Received Data */
# define TMXR_DBG_RET    0x0040000    /* Debug Returned Received Data */
# define TMXR_DBG_MDM    0x0080000    /* Debug Modem Signals */
# define TMXR_DBG_CON    0x0100000    /* Debug Connection Activities */
# define TMXR_DBG_ASY    0x0200000    /* Debug Asynchronous Activities */
# define TMXR_DBG_TRC    0x0400000    /* Debug trace routine calls */
# define TMXR_DBG_PXMT   0x0800000    /* Debug Transmit Packet Data */
# define TMXR_DBG_PRCV   0x1000000    /* Debug Received Packet Data */
# define TMXR_DBG_EXP    0x2000000    /* Debug Expect Activities */
# define TMXR_DBG_SEND   0x4000000    /* Debug Send Activities */

/* Modem Control Bits */

# define TMXR_MDM_DTR         0x01    /* Data Terminal Ready */
# define TMXR_MDM_RTS         0x02    /* Request To Send     */
# define TMXR_MDM_DCD         0x04    /* Data Carrier Detect */
# define TMXR_MDM_RNG         0x08    /* Ring Indicator      */
# define TMXR_MDM_CTS         0x10    /* Clear To Send       */
# define TMXR_MDM_DSR         0x20    /* Data Set Ready      */
# define TMXR_MDM_INCOMING   (TMXR_MDM_DCD|TMXR_MDM_RNG|TMXR_MDM_CTS|TMXR_MDM_DSR)  /* Settable Modem Bits */
# define TMXR_MDM_OUTGOING   (TMXR_MDM_DTR|TMXR_MDM_RTS)  /* Settable Modem Bits */

/* Unit flags */

# define TMUF_V_NOASYNCH   (UNIT_V_UF + 12)             /* Asynch Disabled unit */
# define TMUF_NOASYNCH     (1u << TMUF_V_NOASYNCH)      /* This flag can be defined */
                                                        /* statically in a unit's flag field */
                                                        /* This will disable the unit from */
                                                        /* supporting asynchronous mux behaviors */
/* Receive line speed limits */

# define TMLN_SPD_50_BPS     200000 /* usec per character */
# define TMLN_SPD_75_BPS     133333 /* usec per character */
# define TMLN_SPD_110_BPS     90909 /* usec per character */
# define TMLN_SPD_134_BPS     74626 /* usec per character */
# define TMLN_SPD_150_BPS     66666 /* usec per character */
# define TMLN_SPD_300_BPS     33333 /* usec per character */
# define TMLN_SPD_600_BPS     16666 /* usec per character */
# define TMLN_SPD_1200_BPS     8333 /* usec per character */
# define TMLN_SPD_1800_BPS     5555 /* usec per character */
# define TMLN_SPD_2000_BPS     5000 /* usec per character */
# define TMLN_SPD_2400_BPS     4166 /* usec per character */
# define TMLN_SPD_3600_BPS     2777 /* usec per character */
# define TMLN_SPD_4800_BPS     2083 /* usec per character */
# define TMLN_SPD_7200_BPS     1388 /* usec per character */
# define TMLN_SPD_9600_BPS     1041 /* usec per character */
# define TMLN_SPD_19200_BPS     520 /* usec per character */
# define TMLN_SPD_38400_BPS     260 /* usec per character */
# define TMLN_SPD_57600_BPS     173 /* usec per character */
# define TMLN_SPD_76800_BPS     130 /* usec per character */
# define TMLN_SPD_115200_BPS     86 /* usec per character */

typedef struct tmln TMLN;
typedef struct tmxr TMXR;
struct loopbuf {
    int32               bpr;                            /* xmt buf remove */
    int32               bpi;                            /* xmt buf insert */
    int32               size;
    };

struct tmln {
    int                 conn;                           /* line connected flag */
    SOCKET              sock;                           /* connection socket */
    char                *ipad;                          /* IP address */
    SOCKET              master;                         /* line specific master socket */
    char                *port;                          /* line specific listening port */
    int32               sessions;                       /* count of tcp connections received */
    uint32              cnms;                           /* conn time */
    int32               tsta;                           /* Telnet state */
    int32               rcve;                           /* rcv enable */
    int32               xmte;                           /* xmt enable */
    int32               dstb;                           /* disable Telnet binary mode */
    t_bool              notelnet;                       /* raw binary data (no telnet interpretation) */
    uint8               *telnet_sent_opts;              /* Telnet Options which we have sent a DON'T/WON'T */
    int32               rxbpr;                          /* rcv buf remove */
    int32               rxbpi;                          /* rcv buf insert */
    int32               rxbsz;                          /* rcv buffer size */
    int32               rxcnt;                          /* rcv count */
    int32               rxpcnt;                         /* rcv packet count */
    int32               txbpr;                          /* xmt buf remove */
    int32               txbpi;                          /* xmt buf insert */
    int32               txcnt;                          /* xmt count */
    int32               txpcnt;                         /* xmt packet count */
    int32               txdrp;                          /* xmt drop count */
    int32               txbsz;                          /* xmt buffer size */
    int32               txbfd;                          /* xmt buffered flag */
    t_bool              modem_control;                  /* line supports modem control behaviors */
    int32               modembits;                      /* modem bits which are currently set */
    FILE                *txlog;                         /* xmt log file */
    FILEREF             *txlogref;                      /* xmt log file reference */
    char                *txlogname;                     /* xmt log file name */
    char                *rxb;                           /* rcv buffer */
    char                *rbr;                           /* rcv break */
    char                *txb;                           /* xmt buffer */
    uint8               *rxpb;                          /* rcv packet buffer */
    uint32              rxpbsize;                       /* rcv packet buffer size */
    uint32              rxpboffset;                     /* rcv packet buffer offset */
    uint32              rxbps;                          /* rcv bps speed (0 - unlimited) */
    double              rxbpsfactor;                    /* receive speed factor (scaled to usecs) */
# define TMXR_RX_BPS_UNIT_SCALE 1000000.0
    uint32              rxdelta;                        /* rcv inter character min time (usecs) */
    double              rxnexttime;                     /* min time for next receive character */
    uint32              txbps;                          /* xmt bps speed (0 - unlimited) */
    uint32              txdelta;                        /* xmt inter character min time (usecs) */
    double              txnexttime;                     /* min time for next transmit character */
    uint8               *txpb;                          /* xmt packet buffer */
    uint32              txpbsize;                       /* xmt packet buffer size */
    uint32              txppsize;                       /* xmt packet packet size */
    uint32              txppoffset;                     /* xmt packet buffer offset */
    TMXR                *mp;                            /* back pointer to mux */
    char                *serconfig;                     /* line config */
    SERHANDLE           serport;                        /* serial port handle */
    t_bool              ser_connect_pending;            /* serial connection notice pending */
    SOCKET              connecting;                     /* Outgoing socket while connecting */
    char                *destination;                   /* Outgoing destination address:port */
    t_bool              loopback;                       /* Line in loopback mode */
    t_bool              halfduplex;                     /* Line in half-duplex mode */
    t_bool              datagram;                       /* Line is datagram packet oriented */
    t_bool              packet;                         /* Line is packet oriented */
    int32               lpbpr;                          /* loopback buf remove */
    int32               lpbpi;                          /* loopback buf insert */
    int32               lpbcnt;                         /* loopback buf used count */
    int32               lpbsz;                          /* loopback buffer size */
    char                *lpb;                           /* loopback buffer */
    UNIT                *uptr;                          /* input polling unit (default to mp->uptr) */
    UNIT                *o_uptr;                        /* output polling unit (default to lp->uptr)*/
    DEVICE              *dptr;                          /* line specific device */
    EXPECT              expect;                         /* Expect rules */
    SEND                send;                           /* Send input state */
    };

struct tmxr {
    int32               lines;                          /* # lines */
    char                *port;                          /* listening port */
    SOCKET              master;                         /* master socket */
    TMLN                *ldsc;                          /* line descriptors */
    int32               *lnorder;                       /* line connection order */
    DEVICE              *dptr;                          /* multiplexer device */
    UNIT                *uptr;                          /* polling unit (connection) */
    char                logfiletmpl[FILENAME_MAX];      /* template logfile name */
    int32               txcount;                        /* count of transmit bytes */
    int32               buffered;                       /* Buffered Line Behavior and Buffer Size Flag */
    int32               sessions;                       /* count of tcp connections received */
    uint32              poll_interval;                  /* frequency of connection polls (seconds) */
    uint32              last_poll_time;                 /* time of last connection poll */
    uint32              ring_start_time;                /* time ring signal was raised */
    char                *ring_ipad;                     /* incoming connection address awaiting DTR */
    SOCKET              ring_sock;                      /* incoming connection socket awaiting DTR */
    t_bool              notelnet;                       /* default telnet capability for incoming connections */
    t_bool              modem_control;                  /* multiplexer supports modem control behaviors */
    t_bool              packet;                         /* Lines are packet oriented */
    t_bool              datagram;                       /* Lines use datagram packet transport */
    };

int32 tmxr_poll_conn (TMXR *mp);
t_stat tmxr_reset_ln (TMLN *lp);
t_stat tmxr_detach_ln (TMLN *lp);
int32 tmxr_input_pending_ln (TMLN *lp);
int32 tmxr_getc_ln (TMLN *lp);
t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize);
t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte);
void tmxr_poll_rx (TMXR *mp);
t_stat tmxr_putc_ln (TMLN *lp, int32 chr);
t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size);
t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte);
void tmxr_poll_tx (TMXR *mp);
int32 tmxr_send_buffered_data (TMLN *lp);
t_stat tmxr_open_master (TMXR *mp, CONST char *cptr);
t_stat tmxr_close_master (TMXR *mp);
t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, CONST char *cptr, t_bool async);
t_stat tmxr_detach (TMXR *mp, UNIT *uptr);
t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
char *tmxr_line_attach_string(TMLN *lp);
t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits);
t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback);
t_bool tmxr_get_line_loopback (TMLN *lp);
t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_loopback);
t_bool tmxr_get_line_halfduplex (TMLN *lp);
t_stat tmxr_set_line_speed (TMLN *lp, CONST char *speed);
t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll);
t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll);
t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
void tmxr_msg (SOCKET sock, const char *msg);
void tmxr_linemsg (TMLN *lp, const char *msg);
void tmxr_linemsgf (TMLN *lp, const char *fmt, ...);
void tmxr_linemsgvf (TMLN *lp, const char *fmt, va_list args);
void tmxr_fconns (FILE *st, const TMLN *lp, int32 ln);
void tmxr_fstats (FILE *st, const TMLN *lp, int32 ln);
t_stat tmxr_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat tmxr_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_dscln (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
int32 tmxr_rqln (const TMLN *lp);
int32 tmxr_tqln (const TMLN *lp);
int32 tmxr_tpqln (const TMLN *lp);
t_bool tmxr_tpbusyln (const TMLN *lp);
t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc);
t_stat tmxr_activate (UNIT *uptr, int32 interval);
t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime);
t_stat tmxr_change_async (void);
t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd);
t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp);
t_stat tmxr_startup (void);
t_stat tmxr_shutdown (void);
t_stat tmxr_start_poll (void);
t_stat tmxr_stop_poll (void);
void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize);

# define tmxr_debug(dbits, lp, msg, buf, bufsize) \
    do { if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) \
        _tmxr_debug (dbits, lp, msg, buf, bufsize); } while (0)

# define tmxr_debug_msg(dbits, lp, msg) \
    do { if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) \
        sim_debug (dbits, (lp)->mp->dptr, "%s", msg); } while (0)

# define tmxr_debug_return(lp, val) \
    do { if (sim_deb && (val) && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_RET & (lp)->mp->dptr->dctrl)) \
        { if ((lp)->rxbps) sim_debug (TMXR_DBG_RET, (lp)->mp->dptr, "Ln%d: 0x%x - Next after: %.0f\r\n", \
                (int)((lp)-(lp)->mp->ldsc), val, (lp)->rxnexttime); \
          else sim_debug (TMXR_DBG_RET, (lp)->mp->dptr, "Ln%d: 0x%x\r\n", (int)((lp)-(lp)->mp->ldsc), val); \
        } } while (0)

# define tmxr_debug_trace(mp, msg) \
    do { if (sim_deb && (mp)->dptr && (TMXR_DBG_TRC & (mp)->dptr->dctrl)) \
        sim_debug (TMXR_DBG_TRC, mp->dptr, "%s\r\n", (msg)); } while (0)

# define tmxr_debug_trace_line(lp, msg) \
    do { if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_TRC & (lp)->mp->dptr->dctrl)) \
        sim_debug (TMXR_DBG_TRC, (lp)->mp->dptr, "Ln%d:%s\r\n", (int)((lp)-(lp)->mp->ldsc), (msg)); } while (0)

# define tmxr_debug_connect(mp, msg) \
    do { if (sim_deb && (mp)->dptr && (TMXR_DBG_CON & (mp)->dptr->dctrl)) \
        sim_debug (TMXR_DBG_CON, mp->dptr, "%s\r\n", (msg)); } while (0)

# define tmxr_debug_connect_line(lp, msg) \
    do { if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_CON & (lp)->mp->dptr->dctrl)) \
        sim_debug (TMXR_DBG_CON, (lp)->mp->dptr, "Ln%d:%s\r\n", (int)((lp)-(lp)->mp->ldsc), (msg)); } while (0)

# define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, FALSE)

# if (!defined(NOT_MUX_USING_CODE))
#  define sim_activate tmxr_activate
#  define sim_activate_after tmxr_activate_after
#  define sim_clock_coschedule tmxr_clock_coschedule
#  define sim_clock_coschedule_abs tmxr_clock_coschedule_abs
#  define sim_clock_coschedule_tmr tmxr_clock_coschedule_tmr
#  define sim_clock_coschedule_tmr_abs tmxr_clock_coschedule_tmr_abs
# endif

#endif /* _SIM_TMXR_H_ */
